package Enea.server;

//Import generali.
import Enea.project.dcc.DCC;
import Enea.project.*;
import java.net.*;
import java.io.*;

//Import per logging.
import java.util.logging.Level;
import java.util.logging.Logger;

//Import per XML
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

/**
* Questa classe si occupa di gestire le connessioni accettate da EneaServer. 
* La classe si occupa di comunicare con il DCC per effettuare le operazioni 
* chieste dal client.
*
* @author Davide Aversa
* @version 0.1
*/
public class EneaServerConnection extends Thread {
    
    private Socket connection;
    private DCC project;
    private Document request;

    /**
    * Costruttore della classe.
    *
    * @param connection Indica l'oggetto della classe Socket associato alla connessione in ingresso.
    * @param project Indica il riferimento al progetto di cui si occupa l'istanza della classe.
    */
    public EneaServerConnection(Socket connection, DCC project) {
        this.connection = connection;
        this.project = project;
    }

    @Override
    public void run() {
        try {
            System.out.println("Incoming Connection...");

            //Inizializza il DOM per la ricezione del pacchetto.
            DocumentBuilderFactory docBuilderFact = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docBuilderFact.newDocumentBuilder();
            
            //Crea il documento a partire dal socket in ingresso.
            request = docBuilder.parse(connection.getInputStream());

            //Passa al metodo incaricato di interpretarlo.
            readIncomingRequest();
            
        }
        catch (ParserConfigurationException ex) {
            this.sendMessage5x(505);
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        }        
        catch (IOException ex) {
            this.sendMessage5x(505);
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        }        
        catch (SAXException ex) {
            this.sendMessage5x(505);
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        }        
    }
    
    /**
     * Metodo che si occupa di leggere, interpretare e smistare il messaggio
     * inviato dal client.
     * 
     */
    private void readIncomingRequest() {
        
        Element r = request.getDocumentElement();
        
        //Verifica la presenza del campo "code" nel messaggio.
        NodeList list = request.getElementsByTagName("code");
        if (list.getLength()!=1) this.sendMessage5x(504);
        
        int code = Integer.parseInt(list.item(0).getTextContent());

        System.out.println("Request code: " + code);
        
        if (code == 100) this.registerClient();
        
        if (code == 200) this.takeWorkUnit();
        
        if (code == 300) this.saveResult();
        
        if (code == 400) this.excangeStats();
        //TODO: Completare readIncomingReqeust.
    }
    
    /**
     * Metodo che si occupa di inoltrare la richiesta di registrazione del 
     * Client verso il progetto.
     */
    private void registerClient() {
        System.out.println("Richiesta Registrazione");
        ClientData client = WUTPServerReader.readMessage1x(request);
        project.registerNewClient(client);
        client.setAddress(connection.getRemoteSocketAddress());
        this.sendMessage1x(101,client);
    }
    
    /**
     * Metodo che richiede al progetto una work unit e la consegna al client.
     */
    private void takeWorkUnit() {
        int clientid = WUTPServerReader.readMessage2x(request);
        WorkUnit wu = project.giveMeWork(project.getClientByID(clientid));
        this.sendMessage2x(201, wu);
    }
    
    /**
     * Metodo che consegna al progetto la Result Unit inviata dal client.
     */
    private void saveResult() {
        int clientid;
        NodeList temp = request.getElementsByTagName("clientid");
        
        if (temp.getLength()!=1) clientid = -1;
        else clientid = Integer.parseInt(temp.item(0).getTextContent());

        ResultsPack resutl = WUTPServerReader.readMessage3x(request);
        project.takeResults(project.getClientByID(clientid), resutl);

    }
    
    /**
     * Metodo che scambia le statistiche fra client e server.
     */
    private void excangeStats() {
        ClientStats clientstats = WUTPServerReader.readMessage4x(request);
        project.saveClientStats(clientstats);
        this.sendMessage4x(401, project.giveMeStats());
    }
    
    /**
     * Invia al client un messaggio del tipo 1XX.
     * 
     * @param code Il codice del messaggio da inviare.
     * @param client Il client appena registrato.
     */
    private void sendMessage1x(int code, ClientData client) {
        try {
            Document tosend;
            XMLSerializer serializer = new XMLSerializer();

            switch (code) {
                case 101:
                    tosend = WUTPServerWriter.createMessage1x(code, project.getID(), client);
                    serializer.setOutputByteStream(connection.getOutputStream());
                    serializer.serialize(tosend);
                    break;
            }
        } catch (IOException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * Invia al client un messaggio del tipo 2XX.
     * 
     * @param code Il codice del messaggio da inviare.
     * @param wu La WorkUnit da inviare al client.
     */
    private void sendMessage2x(int code, WorkUnit wu) {
        try {
            Document tosend;
            XMLSerializer serializer = new XMLSerializer();

            switch (code) {
                case 201:
                    tosend = WUTPServerWriter.createMessage2x(code, project.getID(), wu);
                    serializer.setOutputByteStream(connection.getOutputStream());
                    serializer.serialize(tosend);
                    break;
            }
        } catch (IOException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * Invia al client un messaggio del tipo 3XX.
     * 
     * @param code Il codice del messaggio da inviare.
     */
    private void sendMessage3x(int code) {
        try {
            Document tosend;
            XMLSerializer serializer = new XMLSerializer();

            switch (code) {
                case 301:
                    tosend = WUTPServerWriter.createMessage3x(code, project.getID());
                    serializer.setOutputByteStream(connection.getOutputStream());
                    serializer.serialize(tosend);
                    break;
            }
        } catch (IOException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private void sendMessage4x(int code, GlobalStats global) {
        try {
            Document tosend;
            XMLSerializer serializer = new XMLSerializer();

            switch (code) {
                case 401:
                    tosend = WUTPServerWriter.createMessage4x(code, project.getID(), global);
                    serializer.setOutputByteStream(connection.getOutputStream());
                    serializer.serialize(tosend);
                    break;
            }
        } catch (IOException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * Invia al client un messaggio del tipo 5XX.
     * 
     * @param code Il codice del messaggio da inviare.
     */
    private void sendMessage5x(int code) {
        try {
            Document tosend;
            XMLSerializer serializer = new XMLSerializer();

            switch (code) {
                case 501:
                    tosend = WUTPServerWriter.createMessage5x(code);
                    serializer.setOutputByteStream(connection.getOutputStream());
                    serializer.serialize(tosend);
                    break;
                //TODO: fare altri switch casa.
            }
        } catch (IOException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(EneaServerConnection.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}